<!DOCTYPE html>
<html lang="zh-cn">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">




<title>HTML5 Canvas核心技术 图形，动画与游戏开发 | Miles`s</title>

<link rel="stylesheet" href="https://mada.gitee.io/notes//css/styles.css">

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" 
integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>


<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/zenburn.min.css" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js" integrity="sha256-/BfiIkHlHoVihZdc6TFuj7MmJ0TWcWsMXkeDFwhi0zw=" crossorigin="anonymous"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script src="https://mada.gitee.io/notes//js/highlight.js"></script>






<div class="container">
    <nav class="navbar level">
      <div class="navbar-brand">
          <a class="nav-item" href="https://mada.gitee.io/notes/"><h1 class="title is-3">Miles`s</h1></a>
      </div>           
      <div class="navbar-menu has-text-centered is-active">
          <div class="navbar-end is-centered">
              
           </div>
      </div>
    </nav>
  </div>
<div class="container">
  <h2 class="subtitle is-6">June 21, 2016</h2>
  <h1 class="subtitle is-size-4-mobile is-size-3-desktop">HTML5 Canvas核心技术 图形，动画与游戏开发</h1>
  <div class="content">
    

<h2 id="html5-canvas核心技术-图形-动画与游戏开发">HTML5 Canvas核心技术 图形，动画与游戏开发</h2>

<h3 id="canvas-基础">canvas 基础</h3>

<p>Note:</p>

<ul>
<li>canvas只有两个属性 width 和 height</li>
<li>在设置canvas的宽度和高度时，不能使用px后缀</li>
<li>默认的canvas元素大小是300*150个屏幕像素</li>
<li>通过width，height属性设置和css设置并不一样。

<ul>
<li>canvas元素实际上有两套尺寸，一个是元素本身的大小，还有一个是元素绘画表面（drawing surface）的大小。</li>
<li>当设置元素的width和height属性时，实际上是同时修改了该元素本身的大小与绘图表面的大小。</li>
<li>用css设置只会改变元素的大小。</li>
<li>设置了css大小，浏览器会对绘图表面进行缩放，使其符合元素的大小。</li>
</ul></li>
</ul>

<h4 id="canvas元素的api">canvas元素的api</h4>

<ul>
<li>width：元素与绘图表面宽度，非负整数。</li>
<li>height：元素与绘图表面高度，非负整数。</li>
<li>getContext():返回相关的绘图环境对象，</li>
<li>toDataURL(type, quality):返回一个数据地址，可以将它设定为img元素的src属性值。</li>
<li>toBlob(callback, type, args&hellip;):创建一个用于表示此canvas元素图像文件的blob，</li>
</ul>

<h4 id="canvas的绘图环境">canvas的绘图环境。</h4>

<p>仅仅是为了充当绘图环境对象的容器而存在的，该环境对象提供了全部的绘图功能，</p>

<h5 id="2d绘图环境">2d绘图环境</h5>

<p>CanvasRenderingContext2D对象所含的属性</p>

<ul>
<li>canvas： 绘图环境的canvas对象，通常用来获取canvas的高度与宽度</li>
<li>fillstyle：指定绘图环境后续图形填充操作中所使用的颜色，渐变色或图案。</li>
<li>font：字符</li>
<li>globalAlpha：全局透明度设定。将每个像素的alpha值与该值相乘。</li>
<li>globalCompsiteOperation：将某个物体绘制在其他物体之上时，所采用的绘制方式。</li>
<li>lineCap:告诉浏览器如何绘制段落的端点。</li>
<li>lineWidth：绘制线段的屏幕像素宽度。</li>
<li>lineJoin：如何绘制焦点，可取值：bevel，round，miter（默认值）。</li>
<li>miterLimit：miter形式的段落焦点</li>
<li>shadowBlur：如何延伸阴影效果。</li>
<li>shadowColor：阴影颜色。</li>
<li>shadowOffsetX:</li>
<li>shadowOffseY:</li>
<li>strokeStyle:描边</li>
<li>textAlign</li>
<li>textBaseline:垂直对其方式。</li>
</ul>

<h5 id="3d绘画环境的webgl简介">3d绘画环境的webGL简介</h5>

<p>与2d对应的3d绘画环境，叫做webGL。</p>

<h5 id="canvas状态的保存与恢复">canvas状态的保存与恢复</h5>

<ul>
<li>save()：将当前canvas的状态推送到一个保存canvas状态的堆栈顶部。</li>
<li>restore()：将canvas状态堆栈顶部的条目弹出，原来保存于栈顶的那一组状态，在弹出之后，就被设置成canvas当前的状态了。</li>
</ul>

<p>长久以来，开发者一直使用 <code>setInterval</code> 或者 <code>setTimeout</code> 方法来绘制基于网络的动画。
这些方法并不适用于对性能要求很高的动画，应该使用 requestAnimationFrame()方法来取代他们。</p>

<h5 id="控制台与调试器">控制台与调试器</h5>

<ul>
<li>性能分析器（profile）：</li>
<li>时间轴工具（Timeline）：</li>
<li>jsPerf：js性能分析网站</li>
</ul>

<h4 id="事件处理">事件处理</h4>

<h5 id="鼠标事件">鼠标事件</h5>

<pre><code>canvas.onmousedown = function(e){
}
</code></pre>

<p>addEventListener()</p>

<pre><code>canvas.addEventListener('mousedown', function(e){
})
</code></pre>

<p>将鼠标坐标转换为canvas坐标
开发者需要知道发生鼠标事件的点相对于canvas的位置，而不是整个窗口中的位置，所以必须进行坐标转换。</p>

<pre><code>function windowToCanvas(canvas, x, y){
	var bbox = canvas.getBoundingClientRect();
    return {
    	x:x-bbox.left*(canvas.width/bbox.width),
        y:y-bbox.top*(canvas.height/bbox.height)
    }
}
</code></pre>

<h5 id="键盘事件">键盘事件</h5>

<p>canvas 是一个不可获取焦点的元素，在document或window对象上监听事件。</p>

<h5 id="触摸事件">触摸事件</h5>

<h4 id="绘制表面的保存与恢复">绘制表面的保存与恢复</h4>

<p>使用getImageData（） 与 putImageData() 操作图像
立即模式（imediate-mode）绘制图形。不会包含将要绘制的图形对象列表。</p>

<p>example: getImageData() 与 putImageData()实现拖动图形</p>

<pre><code>var canvas = document.getElementById('canvas');
var contexst = canvas.getContext('2d');

function saveDrawingSurface(){
	drawingSurfaceImageData = context.getImageData(0, 0, cavas.width, canvas.height);
}

function restoreDrawingSurface(){
	context.putImageData(drawingSurfaceImageData, 0, 0);
}

canvas.onmousedown = function(e){
	saveDrawingSurface();
}
canvas.onmousemove = function(e){
	var loc = windowToCanvas(e);
    if (dragging){
    	restoreDrawingSurface();
        
        if(guidewires){
        	drawGuidewires(mousedown.x, mousedown.y);
        }
    }
}
canvas.mouseup = function(e){
	restoreDrawingSurface();
}
</code></pre>

<h4 id="在canvas中使用html元素">在canvas中使用html元素</h4>

<p>为了让html控件看上去好像是出现在canvas范围内，可以使用css将这些控件放置在canvas之上。</p>

<h4 id="不可见的html元素">不可见的html元素</h4>

<p>在用户拖动鼠标时动态地修改div元素的大小。</p>

<h5 id="离屏canvas">离屏canvas</h5>

<p>把背景存储在一个或多个离屏canvas之中，并将这些离屏canvas中的某一部分复制到屏幕上，以此大幅提高应用程序的性能。</p>

<h5 id="基础数学知识简介">基础数学知识简介</h5>

<ul>
<li>求解代数方程</li>
<li>三角函数</li>
<li>正弦，余弦与正切函数</li>
<li>向量运算</li>
<li></li>
</ul>

<h3 id="绘制">绘制</h3>

<h4 id="坐标系统">坐标系统</h4>

<p>坐标系统的变换是canvas之中一项非常基本的功能。</p>

<h4 id="canvas的绘制模型">canvas的绘制模型</h4>

<p>在canvas上绘制图形或图像时，浏览器要按照如下步骤来操作：
- 将图形或图像绘制到一个无限大的透明位图中，在绘制时遵从当前的填充模式、描边模式以及线条样式。
- 将图形或图像的阴影绘制到另外一副位图中，在绘制时使用当前绘制环境的阴影设定。
- 将阴影中每一个像素的alpha分量乘以绘图环境对象的globalAlpha属性值。
- 将绘有阴影的位图与经过剪辑区域剪切过的canvas进行图像合成，在操作时使用当前合成模式参数。
- 将图形或图像的每一个像素颜色分量，乘以绘图环境对象的globalAlpha属性值。
- 将绘有图形或图像的位图，合成到当前经过剪辑区域剪切过的canvas位图之上，在操作时使用当前的合成操作符。</p>

<h4 id="矩形的绘制">矩形的绘制</h4>

<p>canvas的api提供了如下三个方法，分别用于矩形的消除，描边及填充：</p>

<ul>
<li><p>clearRect(double x, double y, double w, double h)</p>

<ul>
<li>所谓清除像素，指的是将其颜色设置成为全透明的黑色。
<br /></li>
</ul></li>

<li><p>strokeRect(double x, double y, double w, double h)</p>

<ul>
<li>如果宽度或者高度有一个为0 将分别绘制一条竖线或横线，如果都为0，着不绘制任何东西。</li>
</ul></li>

<li><p>fillRect(double x, double y, double w, double h)</p></li>
</ul>

<h4 id="颜色与透明度">颜色与透明度</h4>

<p>strokeStyle 与 fillstyle 的属性值可以是任意有效的css颜色字串。rgb，rgba，hsl，hsla</p>

<p>note：rgb方式有两个缺陷：</p>

<ul>
<li>以硬件为导向。基于“阴极射线管”。（显像管）</li>
<li>不直观。</li>
</ul>

<p>globalAlpha:默认值1.0 全局透明度</p>

<h4 id="渐变色与图案">渐变色与图案</h4>

<ul>
<li>线性渐变色：createLinearGradient() 返回CanvasGradient实例。</li>
</ul>

<p>addColorStop() 来向渐变色中增加5个“颜色停止点”
- 放射渐变：createRadialGradient()</p>

<p>example:</p>

<pre><code>gradient = context.createRadialGradient(canvas.width/2, canvas.height/2, 10, canvas.width/2, 0, 100);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(.25, 'white');
...
context.fillStyle = gradient;
...

</code></pre>

<h5 id="图案">图案</h5>

<p>允许使用图案来对图形和文本进行描边与填充。图案：image元素，canvas元素或video元素。
createPattern(image, repeat):创建一个可以用来在canvas之中对图形和文本进行描边与填充的图案。</p>

<h4 id="阴影">阴影</h4>

<p>通过修改绘图环境中的如下4个属性值来指定阴影效果：</p>

<ul>
<li>shadowColor:</li>
<li>shadowOffsetX:</li>
<li>shadowOffsetY:</li>
<li>shandowBlur:</li>
</ul>

<p>内嵌阴影：设置shadowOffsetX和shadowOffsetY属性值非0的正整数。
可以使用clip()方法显示超出部分的显示。</p>

<p>Note：阴影肖国栋额绘制可能好耗时：尤其动画效果元素的阴影。</p>

<h4 id="路径-描边与填充">路径、描边与填充</h4>

<p>绘制图形的方法都是基于路径的（path）。
首先调用 <code>beginPath()</code> 方法来开始一段新的路径，rect() 与 arc() 方法分别用于创建矩形及弧形路径。不是封闭的路径，调用 <code>closePath()</code>方法才行。</p>

<h5 id="路径与子路径">路径与子路径</h5>

<p>在某一时刻， canvas之中只能有一条路径存在，canvas规范将其称为“当前路径”。然而，这条路径可以包含许多个子路径（subpath）。</p>

<p>example:</p>

<pre><code>context.beginPath();
context.rect(10,10,100,100);
context.stroke();
context.beginPath();
context.rect(50,50,100,100);
context.stroke();
</code></pre>

<p>Note: 如果不加第二个beginPath 第二次的stroke 将使得当前路径中的两条子路径都被描边，这意味着它会重绘第一个矩形。</p>

<h5 id="填充路径时使用的-非零环绕规则">填充路径时使用的“非零环绕规则”</h5>

<p>如果当前路径是循环的，或是包含多个相交的子路径，那么canvas的绘图环境变量就使用“非零环绕规则”。</p>

<p>非零环绕规则：
1. 对给定区域画一条足够长的线段，使其线段的终点完全落在路径范围之外。
2. 将计数器初始化为0， 然后，每当这条线段与路径上的直线或曲线相交时，就改变计数器的值。
3. 与路径的顺时针部分相交，则+1。
4. 与路径的逆时针部分相交，则-1。
5. 如果计数器的最终结果不是0，那么此区域就在路径里面，在调用fill方法是，浏览器就会对其进行填充。</p>

<h5 id="剪纸效果">剪纸效果</h5>

<p>绘制两个园，使用顺时针绘制内部园，逆时针绘制外部圆，对其填色，产生剪纸效果。</p>

<p>Note：当使用arc方法向当前路径中添加子路径时，该方法必须将上一条子路径的终点与说话圆弧的起点相连。</p>

<p>arc方法可以控制路径的方向，而rect方法则不行。可以使用path或line代替</p>

<h4 id="线段">线段</h4>

<p>方法：moveTo 和 lineTo
使用stroke方法绘制。</p>

<h4 id="线段与像素边界">线段与像素边界</h4>

<ol>
<li>在某2个像素的边界处绘制一条1像素的线段，那么该线段实际上会占据2个像素的宽度。</li>
<li>如果在像素边界出绘制一条1像素的垂直线段，那么canvas的绘图环境对象会试着将半个像素在边界中线的右边，将另一半像素绘制在左边。</li>
</ol>

<h5 id="网格的绘制">网格的绘制。</h5>

<p>Note：</p>

<h5 id="橡皮筋式选取框">橡皮筋式选取框</h5>

<p>example：</p>

<h5 id="虚线的绘制-drawdashedline-context-context-canvas-width-context-height-20-context-canvas-height-20-14">虚线的绘制：drawDashedLine(context, context.canvas.width, context.height, 20, context.canvas.height-20, 14);</h5>

<h5 id="通过扩展canvasrenderingcontext2d来绘制虚线">通过扩展CanvasRenderingContext2D来绘制虚线</h5>

<h4 id="线段与连接点的绘制">线段与连接点的绘制</h4>

<p>lineCap 线帽：bevel|miter|round</p>

<h4 id="圆弧与圆形的绘制">圆弧与圆形的绘制</h4>

<p>arc：arc(x, y, radius, startAngle, endAngle, counterClockwise)</p>

<p>以橡皮筋式辅助线来协助画圆：
example：TODO</p>

<h5 id="arcto-方法">arcTo 方法</h5>

<p>arcTo(x1，y1，x2，y2，radius)</p>

<h4 id="贝塞尔曲线">贝塞尔曲线</h4>

<ul>
<li>二次贝塞尔曲线：二次曲线，两个锚点一个控制点。
<code>quadraticCurveTo(cx, cy, ex, ey)</code> 控制点， 锚点</li>
<li>三次方贝塞尔曲线：<code>bezierCurveTo(double cpx, double cpy, double cp2x, double cp2y, double x, double y)</code></li>
</ul>

<h4 id="多边形的绘制">多边形的绘制</h4>

<p>moveTo 和 lineTo 的结合</p>

<p>createPolygonPath 创建多个点的多边形</p>

<p>example：拖动多边形对象</p>

<p>编辑贝塞尔曲线：</p>

<h4 id="自动滚动网页-使其段落路径所对应的元素显示在窗口中">自动滚动网页，使其段落路径所对应的元素显示在窗口中</h4>

<p>scrollPathIntoView：</p>

<h4 id="坐标变换">坐标变换</h4>

<p>对canvas坐标系统进行移动，旋转，缩放等操作。</p>

<h4 id="坐标系的平移-缩放与旋转">坐标系的平移、缩放与旋转</h4>

<p>rotate（double angleInRadians）
scale（double x， double y）
translate（double x， double y）</p>

<h5 id="镜像">镜像</h5>

<pre><code>drawArrow(context)
context.translate(canvas.width, 0)
context.scale(-1, 1)
drawArrow(context)

</code></pre>

<h4 id="图像合成">图像合成</h4>

<p>在canvas中将某个物体绘制在一个物体之上，那么浏览器就会简单的把原物体的图像叠放在目标物体的图像上面。
globalCompositeOperation的属性改变默认的图像合成行为。</p>

<h4 id="剪辑区域">剪辑区域</h4>

<p>clip</p>

<h5 id="通过剪辑区域来擦除图像">通过剪辑区域来擦除图像</h5>

<p><code>clearRect(0,0,canvas.width, canvas.height)</code></p>

<h3 id="文本">文本</h3>

<p>文本对象有3个主要方法：</p>

<ul>
<li>strokeText(text, x, y)</li>
<li>fillText(text, x, y)</li>
<li>measureText(text): 返回一个名为width的属性。表示该文本所占用的宽度。</li>
</ul>

<p>canvas文本对象有3个相关属性：</p>

<ul>
<li>font</li>
<li>textAlign</li>
<li>textBaseline</li>
</ul>

<h5 id="子啊圆弧周围绘制文本">子啊圆弧周围绘制文本</h5>

<p>步骤：</p>

<ul>
<li>计算圆弧周围每个字符的绘制坐标</li>
<li>将坐标系平移至绘制字符的地点</li>
<li>将坐标系旋转PI/2-angle度</li>
<li>对字符进行描边或填充操作</li>
</ul>

<p>Note： 应用程序必须先调用 translate方法后调用rotate方法。</p>

<h3 id="图像与视频">图像与视频</h3>

<p>canvas的绘图环境提供了4个用于绘图及操作的方法：</p>

<ul>
<li>drawImage()</li>
<li>getImageData()</li>
<li>putImageData()</li>
<li>createImageData()</li>
</ul>

<p>drawImage 可以将另外一个canvas的内容或者视频的其中一帧绘制到当前canvas之中。</p>

<h4 id="在canvas中绘制图像">在canvas中绘制图像</h4>

<pre><code>var image = new Image()
image.src = &quot;aa&quot;
image.onload = function(){
	context.drawImage(image, 0, 0)
}

</code></pre>

<p>该方法缺点：必须等待图片加载完成之后才能进行绘制，如果尚未完成加载就进行绘制，会执行失败，而且没有任何提示。</p>

<p>drawImage 方法：</p>

<ul>
<li>drawImage(image, dx, dy)</li>
<li>drawImage(image, dx, dy, dw, dh)</li>
<li>drawImage(iamge, sx, sy, sw, sh, dx, dy, dw, dh)</li>
</ul>

<p>可以在canvas范围之外绘制图像：图片放大
按比例系数，计算图片显示位置</p>

<h4 id="将一个canvas绘制到另一个canvas之中">将一个canvas绘制到另一个canvas之中。</h4>

<p>使用离屏canvas，通常需要遵循4个步骤：</p>

<ol>
<li>创建用作离屏canvas的元素。</li>
<li>设置离屏canvas的高度和宽度。</li>
<li>在离屏canvas之中进行绘制。</li>
<li>将离屏canvas的全部或者一部分内容复制到正在显示的canvas之中。</li>
</ol>

<p>Note： 离屏canvas会占据一定的内存，不过他们可以显著的提高绘图效率。</p>

<h3 id="操作图像的像素">操作图像的像素</h3>

<h4 id="获取图像数据">获取图像数据</h4>

<p>getImageData()    putImageData()</p>

<p>ImageData 对象：</p>

<ul>
<li>width：设备像素为单位的图像数据宽度。</li>
<li>height：设备像素为单位的图像数据高度。</li>
<li>data： 包含各个设备像素数值的数组。</li>
</ul>

<p>getImageData():运行的速度比较慢，每次检测到鼠标按下事件的时候，才调用一次getImageData()。</p>

<p>Note：putImageData()不受全局设置的影响。</p>

<h4 id="修改图像数据">修改图像数据</h4>

<p>ImageData对象中的数组：data属性指向一个包含8位二进制整数的数组，这些整数的值位于0~255之间，分别表示一个像素的红、绿、蓝及透明度分量。</p>

<pre><code>    for(i=3; i&lt;imageData.data.length-4;i += 4 ){
        imageData.data[i] = imageData.data[i] / 2 // Alpha
        imageData.data[i+1] = imageData.data[i+1] // red
        imageData.data[i+2] = imageData.data[i+2] // green
        imageData.data[i+3] = imageData.data[i+3] // blue
    }
</code></pre>

<h5 id="图像滤镜">图像滤镜</h5>

<p>从255之中减去每个像素的分量值。</p>

<p>example：浮雕滤镜</p>

<h5 id="用工作线程处理图像-webworker">用工作线程处理图像（webworker）</h5>

<p><code>var sunglassFilter = new Worker(&quot;sunglassFilter.js&quot;)</code> 创建一个工作线程。传递给worder构造器的文件名表示工作线程将要执行的javascript文件。</p>

<p>将耗时的代码放在工作线程中，及时响应用户操作
封装算法。</p>

<h4 id="结合剪辑区域来绘制图像">结合剪辑区域来绘制图像</h4>

<p>图像渐渐淡出：设置alpha值 或者设置globalAlpha</p>

<p>离屏canvas制作动画：</p>

<h4 id="性能">性能</h4>

<p>drawImageData 要比putImageData快</p>

<p>提高效率方法：</p>

<ol>
<li>避免在循环体内直接范围对象的属性， 放在局部变量中。</li>
<li>应该用循环计数器来遍历完整的像素，而非像素分量。</li>
<li>逆向遍历与移位技巧的效果并不好。</li>
<li>不要频繁的调用getImageData()来获取数据。</li>
</ol>

<h4 id="放大镜">放大镜</h4>

<p>步骤：</p>

<ol>
<li>调用putImageData将上一次背景图像回复到canvas</li>
<li>调用getImageData捕捉当前位置image</li>
<li>将剪辑区域设定为放大镜所在范围</li>
<li>调用drawImage方法，将放大后的图像绘制到canvas中</li>
<li>绘制放大镜的镜片。</li>
</ol>

<h3 id="动画">动画</h3>

<h4 id="动画循环">动画循环</h4>

<p>Note：不要使用<code>setInterval</code>或者<code>setTimeOut</code>来做动画。并不能提供制作动画所需要的精确计时机制。它们只是让应用程序能在某个大致时间运行而已。</p>

<p>不应主动命令浏览器何时去绘制下一帧动画，这应该有浏览器来通知你：使用requerstAnimationFrame</p>

<h5 id="通过requestanimationframe让浏览器自行决定帧速度">通过requestAnimationFrame让浏览器自行决定帧速度。</h5>

<p>不使用<code>setInterval</code>和<code>setTimeOut</code>方法原因：</p>

<ul>
<li>他们都是通用方法，并不是专门制作动画而用。</li>
<li>即使向其传递以毫秒为单位的参数值，他们也达不到毫秒级的精确性。</li>
<li>没有对调用动画循环的机制作优化。</li>
<li>不考虑绘制动画的最佳时机，而只是一味的以某个大致的时间间隔来调用动画循环。</li>
</ul>

<p>返回：传递一个时间值。</p>

<p>polyfill 多端支持。</p>

<h4 id="帧速度的计算">帧速度的计算</h4>

<p>计算：</p>

<pre><code>    var lastTime = 0;
    function calculateFps(){
        var now = (+new Date),fps = 1000/(now - lastTime);
        lastTime = now;
        return fps;
    }

    function animate(time){
        eraseBackground();
        drawBackground();
        update();
        draw();

        context.fillStyle = 'cornflowerblur';
        context.fillText(calculateFps().toFixed() + ' fps' , 20, 60);
        window.requestNextAnimationFrame(animate);
    }
    window.requestNextAnimationFrame(animate);
</code></pre>

<h4 id="以不同的帧速率来执行各种任务">以不同的帧速率来执行各种任务</h4>

<h4 id="恢复动画背景">恢复动画背景</h4>

<p>三种办法：</p>

<ol>
<li>将内容全部擦除，并重新绘制</li>
<li>仅重绘内容发生变化的那部分区域</li>
<li>从离屏缓存区中将内容发生变化的那部分背景图像复制到屏幕上。</li>
</ol>

<h5 id="利用剪辑区域来处理动画背景">利用剪辑区域来处理动画背景</h5>

<p>如果背景图像很简单，可以全部擦除然后重绘。如果背景复杂，使用离屏</p>

<ol>
<li>调用context.save(), 保存canvas状态</li>
<li>调用beginPath开始新路径</li>
<li>在context对象上调用arc rect 等方法设置路径</li>
<li>调用context.clip 剪辑区域</li>
<li>擦除canvas中的图像</li>
<li>将背景图像绘制到屏幕canvas中（绘制操作实际上只会影响剪辑区域所在范围）</li>
<li>恢复canvas的状态参数，该操作主要是为了重置剪辑区域。</li>
</ol>

<h5 id="利用图块复制技术来处理动画背景">利用图块复制技术来处理动画背景</h5>

<p>将整个背景一次性的复制到离屏canvas中，稍后从离屏canvas中只将修复动画背景所需的那一块图像复制到屏幕中即可。</p>

<p>优缺点：
- 剪辑区域：
- 图块复制技术：速度快，但是占据内存</p>

<h4 id="双缓冲技术">双缓冲技术</h4>

<p>单缓冲产生闪烁：先讲东西绘制在离屏上  然后一次性的复制到canvas中。
双缓冲技术可以有效的消除动画闪烁，浏览器会自动采用双缓冲实现canvas元素。开发者不需要自己实现。</p>

<p>检测双环从技术：</p>

<pre><code>    function animate(now){
        eraseBackground();
        for(var i=0; i&lt;500000;++i){
            sum +=i;
        }
        drawBackground()
        draw()
        requestNextAnimationFrame(time)
    }
    requestNextAnimationFrame(time)
</code></pre>

<h4 id="基于时间的运动">基于时间的运动</h4>

<p>使动画在所有情况下都以相同速度播放。不受帧速率的影响。
根据物体的速度计算出两帧之间所移动的像素数。</p>

<pre><code>像素/帧 = 像素/秒 * 帧/秒
</code></pre>

<h4 id="背景的滚动">背景的滚动</h4>

<p>通过移动canvas绘图环境对象的原点坐标来实现背景滚动效果：</p>

<pre><code>	var SKY_VELOCITY = 30,skyOffset = 0;
    
    function draw(){
    	skyOffset = skyOffset &lt; canvas.width?skyOffset + SKY_VELOCITY:0;
        context.save();
        context.translate(-skyOffset, 0);
        context.drawImage(sky, 0 , 0);
        context.drawImage(sky, sky.width, 0);
        cintext.restore();
    }
</code></pre>

<p>随着绘画环境对象原点的移动，原来位于屏幕外的那副图像渐渐的滚动到屏幕内。</p>

<h4 id="视差动画">视差动画</h4>

<p>让动画图层以不同的速冻滚动，这样就实现了视差效果。</p>

<h4 id="用户手势">用户手势</h4>

<p>用户通过鼠标或者手指触摸来与动画互动。</p>

<h4 id="定时动画">定时动画</h4>

<h5 id="秒表">秒表</h5>

<h5 id="动画计时器">动画计时器</h5>

<h4 id="动画制作的最佳指导原则">动画制作的最佳指导原则</h4>

<ul>
<li>使用类似requstNextAnimationFrame() 这样的polyfill 方法来保持浏览器兼容性。</li>
<li>将业务逻辑的更新与动画的绘制分开</li>
<li>使用基于时间的运动来协调动画的播放速度</li>
<li>用剪辑区域或者图块复制技术将复杂的背景图像恢复到屏幕上。</li>
<li>必要时可使用一个或多个离屏缓冲区提升背景的绘制速度</li>
<li>不要手工实现传动的双缓冲算法：浏览器会自动实现</li>
<li>不要通过css指定阴影及圆角效果</li>
<li>不要在canvas中进行带阴影效果的绘制操作</li>
<li>不要在播放动画时分配内存</li>
<li>使用性能调试及时间轴工具来监控并改善动画的绘制效率。</li>
</ul>

<h3 id="精灵">精灵</h3>

<p>要制作一个精灵对象，必须让开发者能把他们绘制出来，能够将其放置于动画中的制定位置，并且能以给定的速度将其从一个地方移动到另一个地方。这些精灵或许还能接受调用者的命令，来执行某些特定的动作。</p>

<p>精灵对象有两个方法：paint 和 update
构造器：精灵名称，绘制器及行为数组。</p>

<pre><code>    var Sprite = function(name, painter, behaviors){
        if (name !== undefined) this.name = name;
        if (painter !== undefined) this.painter = painter;
        this.top = 0;
        this.left = 0;
        this.width = 10;
        this.height = 10;
        this.velocityX = 0;
        this.velocityY = 0;
        this.visible = true;
        this.animation = false;
        this.behaviors = behaviors || {};
        return this;
    }
    
    Sprite.prototype = {
    	paint : function(context){
        	if(this.painter !== undefined &amp;&amp; this.visible){
            	this.painter.paint(this, context);
            }
        },
        update: function(context, time){
            for(var i=0;i&lt;this.behaviors.length;++i){
                this.behaviors[i].execute(this, context, time);
            }
        }
    }
</code></pre>

<h4 id="精灵绘制器">精灵绘制器</h4>

<p>Sprite 对象与绘制其内容的绘制器对象之间是解耦的。在程序运行时动态的设定绘制器。
Painter对象可被归纳为三类：</p>

<ul>
<li>描边及填充绘制器
会调用stroke 与 fill 在内的canvas图形函数来绘制精灵。
享元模式：使用一个对象表示多个概念</li>

<li><p>图像绘制器</p></li>

<li><p>精灵表绘制器
为了节省磁盘空间，减少下载次数，如果制作动画的精灵对象其没帧所用的图像都比较小，那么可以把他们放在一张图片中。
精灵表会把精灵表中表示当前动画帧的那个单元格画出来。调用advance方法可以将索引值加1.</p></li>
</ul>

<h4 id="精灵对象的行为">精灵对象的行为</h4>

<p>只要实现execute(sprite, context, time)方法的对象，都可以叫做 行为。该方法一般会以某种方式来修改精灵的属性，比如移动其位置，或是修改其外观。</p>

<p>精灵含有一个行为对象数组，它的update方法会遍历该数组，使每个行为对象都得以执行一次。</p>

<h4 id="将多个行为组合起来">将多个行为组合起来</h4>

<p>精灵含有一个行为对象数组，所以开发者可以根据需要向任何精灵对象之中添加任意数量的行为对象。精灵的update方法会从数组中的第一个行为对象开始，一直遍历到最后一个对象，依次调用其execute方法。</p>

<p>精灵可以有任意多的行为对象，开发者可以在程序运行时直接操作behaviors数组来增加及移除行为对象。</p>

<h4 id="限时触发的行为">限时触发的行为</h4>

<p>该对象所封装的行为只会在给定的事件段内表现出来。</p>

<h4 id="精灵动画制作器">精灵动画制作器</h4>

<h3 id="移动平台开发">移动平台开发</h3>

<p>移动设备开发技巧</p>

<ul>
<li>通过指定名为<code>viewport</code>的<code>metatag</code> 我们可以根据特定的设备与显示方向优化应用程序的视窗大小。</li>
<li>css媒体查询</li>
<li>以javascript语言编写监听器，检测媒体特征的变更。</li>
<li>处理触摸事件。</li>
<li>禁用惯性滚动功能。</li>
<li>禁止用户缩放应用程序，停用“DIV闪烁”等特征</li>
<li>实现手指缩放功能。</li>
<li>在平板电脑上实现基于canvas的虚拟键盘控件。</li>
</ul>

<p>在ios是那个运行</p>

<ul>
<li>创建应用程序图标及启动图像</li>
<li>使用媒体特征查询技术来选择适当的应用程序图标及启动图像</li>
<li>令html5应用程序直接以全屏模式运行，不显示任何浏览器饰件</li>
<li>设置状态栏的背景色</li>
</ul>

  </div>
</div>
<div class="container has-text-centered">
    
</div>

<div class="container has-text-centered">
  
</div>
<section class="section">
  <div class="container has-text-centered">
    <p></p>
  </div>
</section>


